/*
 * An example showing that any Production may be used as a start rule in chevrotain.
 * There is no artificial limit of which rule may be a start rule.
 *
 * Multiple start rules can be useful in certain contexts, for example:
 * 1. Unit Testing the grammar.
 * 2. Partial parsing of only the modified parts of a document in an IDE.
 */

import { createToken, Lexer, CstParser } from "chevrotain";

// ----------------- lexer -----------------
const Alpha = createToken({ name: "Alpha", pattern: /A/ });
const Bravo = createToken({ name: "Bravo", pattern: /B/ });
const Charlie = createToken({ name: "Charlie", pattern: /C/ });

const WhiteSpace = createToken({
  name: "WhiteSpace",
  pattern: /\s+/,
  group: Lexer.SKIPPED,
});

const allTokens = [
  WhiteSpace, // whitespace is normally very common so it should be placed first to speed up the lexer's performance
  Alpha,
  Bravo,
  Charlie,
];

const PhoneticLexer = new Lexer(allTokens);

// ----------------- parser -----------------
class MultiStartParser extends CstParser {
  constructor() {
    super(allTokens);

    const $ = this;

    $.RULE("firstRule", () => {
      $.CONSUME(Alpha);

      $.OPTION(() => {
        $.SUBRULE($.secondRule);
      });
    });

    $.RULE("secondRule", () => {
      $.CONSUME(Bravo);

      $.OPTION(() => {
        $.SUBRULE($.thirdRule);
      });
    });

    $.RULE("thirdRule", () => {
      $.CONSUME(Charlie);
    });

    // very important to call this after all the rules have been defined.
    // otherwise the parser may not work correctly as it will lack information
    // derived during the self analysis phase.
    this.performSelfAnalysis();
  }
}

// ----------------- wrapping it all together -----------------

// reuse the same parser instance.
const parser = new MultiStartParser();

function parseStartingWithRule(ruleName) {
  return function (text) {
    const lexResult = PhoneticLexer.tokenize(text);
    // setting a new input will RESET the parser instance's state.
    parser.input = lexResult.tokens;
    // just invoke which ever rule you want as the start rule. its all just plain javascript...
    const cst = parser[ruleName]();

    return {
      cst: cst,
      lexErrors: lexResult.errors,
      parseErrors: parser.errors,
    };
  };
}

export const parseFirst = parseStartingWithRule("firstRule");
export const parseSecond = parseStartingWithRule("secondRule");
export const parseThird = parseStartingWithRule("thirdRule");
